#include once "mud-gui/widgets/LineEdit.bi"

namespace ui

constructor LineEdit( byval parent as BaseWidget ptr = 0, byref _text_ as string = "", byref _placeholder_ as string = "" )

	if parent <> 0 then
	
		if parent->cl <> layout_t.canHaveLayout then ShowError("LineEdit widget must have a valid parent.")
		SetParent(parent)

	end if
	
	setText = _text_
	setPlaceHolder = _placeholder_

	(BaseObject).id = ui.widgetids.lineedit
	(BaseObject).cl = layout_t.noLayout
	
	connectSignal(@this,repaint,LineEdit.PaintEvent)
	connectSignal(@this,destroy,LineEdit.Destroy)
	connectSignal(@this,resize,LineEdit.resizeEvent)
	connectSignal(@this,timerExpire,LineEdit.timerEvent)
	connectSignal(@this,keystroke,LineEdit.keystrokeEvent)
	connectSignal(@this,clicked,LineEdit.clickEvent)
	
	addTimer( toBaseWidget(@this), 0.0, timer_e.REPEAT )
	setEditable = 1
	setMultiLine = 0
	
	m_caret = 0
	m_textStartAt = 1
	m_rowStartAt = 0
	m_caretInterval = 0.5
	m_caretNextToggle = Timer + m_caretInterval
	m_selection = -1
	setCaret = 0
	setCaretRow = 0
	
	m_acceptedChars = ""
	for i as integer = 32 to 126 : m_acceptedChars &= chr( i ) : next i
	
	m_rowCount = 1
	m_strRows(0).text = ""
	m_strRows(0).start = 0
	m_strRows(0).length = 0

end constructor

property LineEdit.text( ) as string
	return m_text
end property

property LineEdit.setText( byref _t_ as string )
	m_text = _t_
	countTextRows()
end property

IMP_BYREF_PROPERTY( LineEdit, placeholder, string )

property LineEdit.editable( ) as ubyte
	return m_editable
end property

property LineEdit.setEditable( byval _t_ as ubyte )
	m_editable = _t_
	var e = new _GUI_EVENT_
	e->id = ui.GEVENT.resize
	(BaseObject).process_signals( @(BaseObject), e )
end property

property LineEdit.minimumWidth( ) as integer
	return (BaseObject).minwidth
end property

property LineEdit.setMinimumWidth( byval _t_ as integer )
	(BaseObject).minwidth = _t_
end property

property LineEdit.minimumHeight( ) as integer
	return (BaseObject).minheight
end property

property LineEdit.setMinimumHeight( byval _t_ as integer )
	(BaseObject).minheight = _t_
end property

property LineEdit.columns() as integer
	dim as integer resx
	ScreenInfo( resx )
	var fontWidth = resx / LoWord( width() )
	return ((BaseObject).width-6)/fontWidth
end property

property LineEdit.setColumns( byval _t_ as integer )
	dim as integer resx
	ScreenInfo( resx )
	var fontWidth = resx / LoWord( width() )
	(BaseObject).width = (_t_ * fontWidth)+6
end property

property LineEdit.rows() as integer
	dim as integer resy
	ScreenInfo( 0, resy )
	var fontHeight = resy / HiWord( width() )
	return ((BaseObject).height-6)/fontHeight
end property

property LineEdit.setRows( byval _t_ as integer )
	dim as integer resy
	ScreenInfo( 0, resy )
	var fontHeight = resy / HiWord( width() )
	(BaseObject).height = (_t_ * fontHeight)+6
end property

IMP_PROPERTY( LineEdit, wordwrap, ubyte )
IMP_PROPERTY( LineEdit, isPassword, ubyte )

property LineEdit.passwordChar( ) as string
	return m_passwordChar
end property

property LineEdit.setPasswordChar( byref _t_ as string )
	m_passwordChar = left( _t_, 1 )
end property

property LineEdit.caret() as integer
	return m_caret
end property

property LineEdit.setCaret( byval _t_ as integer )
	/'if _t_ <= 0 then
		m_caret = 0
	elseif _t_ >= 1 then
		if _t_ > columns then _t_ = columns
		dim maxd as integer
		maxd = len( mid( text, m_textStartAt, columns ) )
		if _t_ > maxd then
			m_caret = maxd
		else
			m_caret = _t_
		end if
	end if'/
end property

property LineEdit.caretRow() as integer
	if multiline = 0 then return 0
	for n as integer = m_rowStartAt to (m_rowStartAt+rows)-1
		if m_caret >= m_strRows( n ).start then
			return (n-m_rowStartAt)
		end if
	next n
	return m_rowStartAt
end property

property LineEdit.setCaretRow( byval _t_ as integer )
	var tmp = _t_
	if tmp < 0 then tmp = 0
	if tmp >= m_rowCount then
		ConsolePrint( "Requesting row #" & _t_ & ", giving row #" & m_rowCount-1 )
		tmp = m_rowCount-1
	end if
	var position = iif( caretCol < m_strRows( tmp ).length, caretCol, m_strRows( tmp ).length-1 )
	m_caret = m_strRows( tmp ).start + position
	if tmp < m_rowStartAt then m_rowStartAt = tmp
	if tmp > (m_rowStartAt+rows) then m_rowStartAt = tmp-rows
end property

property LineEdit.caretCol() as integer
	return (m_caret - m_strRows( caretRow ).start) + iif( m_strRows( caretRow ).length > 0, 1, 0 )
end property

property LineEdit.setCaretCol( byval _t_ as integer )
	var position = iif( _t_ <= m_strRows( caretRow ).length, _t_, m_strRows( caretRow ).length )
	m_caret = m_strRows( _t_ ).start + position
end property

''IMP_PROPERTY( LineEdit, caretRow, integer )
IMP_BYREF_PROPERTY( LineEdit, acceptedChars, string )
IMP_PROPERTY( LineEdit, multiLine, ubyte )

STATIC_SLOT(LineEdit.clickEvent)

	var le = cast( LineEdit ptr, ref )
	if le->editable = 1 then
		if setFocus(0) <> ref then
			setFocus(ref)
		end if
		dim as integer resx, resy
		ScreenInfo( resx, resy )
		var w = width()
		var fontWidth = resx / LoWord( w )
		var fontHeight = resy / HiWord( w )
		var x = (e->m_x - ref->x - 1)\fontWidth
		var y = (e->m_y - ref->y - 1)\fontHeight
		ConsolePrint( "Literal cursor row(" & y & "), column(" & x & ")" )
		if multikey( fb.SC_LSHIFT ) Or multikey( fb.SC_RSHIFT ) then
			if ( le->m_selection = -1 ) then le->m_selection = le->m_caret
		else
			le->m_selection = -1
		end if
		le->setCaretRow = y
		le->setCaretCol = x
		ConsolePrint( "Real cursor row(" & le->caretRow & "), column(" & le->caretCol & ")" )
	end if
	return 1

ENDSLOT

STATIC_SLOT(LineEdit.Destroy)

	var le = cast(LineEdit ptr, ref)
	
	le->setText = ""
	le->setPasswordChar = ""
	le->setAcceptedChars = ""
	
	for i as integer = 0 to 255
		le->m_strRows(i).text = ""
	next i
	le->m_rowCount = 1
	
	return 0
	
ENDSLOT

STATIC_SLOT(LineEdit.PaintEvent)

	var le = cast(LineEdit ptr, ref)
	put ( ref->x, ref->y), ref->buffer, PSET
	
	dim as string displayText
	displayText = mid( le->text, le->m_textStartAt, le->columns )
	if le->isPassword then displayText = string( len( displayText ), left( le->passwordChar, 1 ) )
	if le->editable = 1 then
		if len( le->text ) = 0 and ( ui.setFocus(NULL) <> ref ) and len( le->placeholder ) then
			draw string (ref->x+3,ref->y+3), mid( le->placeholder, 1, le->columns), rgb(100,100,100)
		else
			dim as integer resx, resy
			ScreenInfo( resx, resy )
			var w = width()
			var fontWidth = resx / LoWord( w )
			var fontHeight = resy / HiWord( w )
			''if le->m_selection >= 0 then line (ref->x + 3 + (le->m_caret*fontWidth) - 1, ref->y + 3)-(ref->x + 3 + (le->m_selection*fontWidth) - 1, ref->y + 3 + fontHeight), rgb(200,200,255), bf
			if le->multiLine then
				''ConsolePrint( "Drawing Text (multiline:" & le->m_rowCount & ")" )
				var tmpx = ref->x+3
				var tmpy =  ref->y+3
				var index = 1
				for a as integer = le->m_rowStartAt to (le->m_rowStartAt+le->rows)-1
					if a > le->m_rowCount then exit for
					if le->m_strRows(a).start >= le->m_textStartAt then
						draw string (tmpx,tmpy), le->m_strRows(a).text, rgb(0,0,0)
						tmpy += fontHeight
					end if
					if (tmpy/fontHeight) > le->rows then exit for
				next a
			else
				'ConsolePrint( "Drawing Text (no multiline)" )
				draw string (ref->x+3,ref->y+3), displayText, rgb(0,0,0)
			end if
			if le->m_cursor then
				line (ref->x + 3 + (le->caretCol*fontWidth) - 1, ref->y + 3 + (le->caretRow*fontWidth) - 1)-step(1, fontHeight), rgb(0,0,0), bf
			end if
		end if
	else
		dim as integer resx, resy
		ScreenInfo( resx, resy )
		var w = width()
		var fontWidth = resx / LoWord( w )
		var fontHeight = resy / HiWord( w )
		if le->multiLine then
			var tmpx = ref->x+3
			var tmpy =  ref->y+3
			var index = 1
			for a as integer = le->m_rowStartAt to (le->m_rowStartAt+le->rows)-1
				if a > le->m_rowCount then exit for
				draw string (tmpx,tmpy), le->m_strRows(a).text, rgb(50,50,50)
				tmpy += fontHeight
			next a
		else
			draw string (ref->x+3,ref->y+3), displayText, rgb(50,50,50)
		end if
	end if
	
	return 0
	
ENDSLOT

STATIC_SLOT(LineEdit.resizeEvent)

	if ref->buffer = 0 then ref->buffer = imagecreate( ref->width, ref->height, rgb(&hc9,&hc9,&hc9) )

	if cast(LineEdit ptr, ref)->editable = 1 then
		line ref->buffer,(1,1)-(ref->width-2,ref->height-2), rgb(255,255,255), BF
		line ref->buffer,(1,1)-(ref->width-2,ref->height-2), rgb(0,0,0), B
	else
		line ref->buffer,(1,1)-(ref->width-2,ref->height-2), rgb(220,220,220), BF
		line ref->buffer,(1,1)-(ref->width-2,ref->height-2), rgb(0,0,0), B
	end if
	
	return 0

ENDSLOT

STATIC_SLOT(LineEdit.timerEvent)

	var le = cast(LineEdit ptr, ref)
	
	if setFocus( NULL ) <> ref then
		le->m_cursor = 0
	else
		if le->editable = 1 then
			if Timer >= le->m_caretNextToggle then
				le->m_cursor = not le->m_cursor
				le->m_caretNextToggle = Timer + le->m_caretInterval
			end if
		else
			le->m_cursor = 0
		end if
	end if
	
	return 0

ENDSLOT

STATIC_SLOT(LineEdit.keystrokeEvent)

	var le = cast(LineEdit ptr, ref)
	if le->editable = 1 then
	dim as string splitA, splitB
	dim as integer max, min
	select case e->ascii
	case 32 to 126
		if le->m_selection = -1 then
			if le->caret = len( le->text ) then
				le->setText = le->text & chr(e->ascii)
			else
				splitA = Mid( le->text, 1, le->caret )
				splitB = Mid( le->text, le->caret+1 )
				le->setText = splitA & chr(e->ascii) & splitB
			end if
		else
			min = iif( le->caret < le->m_selection, le->caret, le->m_selection )
			max = iif( le->caret > le->m_selection, le->caret, le->m_selection )
			if max = len( le->text ) then
				le->setText = mid( le->text, min-1 ) & chr(e->ascii)
			else
				splitA = Mid( le->text, 1, min-1 )
				splitB = Mid( le->text, max )
				le->setText = splitA & chr(e->ascii) & splitB
			end if
			le->setCaret = min
			le->m_selection = -1
		end if
		le->countTextRows()
		le->moveCaretRight()
	case else
		select case e->scancode
		case fb.SC_ESCAPE
			'todo: send cancel signal
			' or maybe just clear the text?
		case fb.SC_ENTER
			if le->rows = 1 then
				'todo: send activate signal
			else
				if multikey( fb.SC_LSHIFT ) or multikey( fb.SC_RSHIFT ) then
					min = iif( le->caret < le->m_selection, le->caret, le->m_selection )
					max = iif( le->caret > le->m_selection, le->caret, le->m_selection )
					splitA = Mid( le->text, 1, min-1 )
					splitB = Mid( le->text, max )
					le->setText = splitA & chr(13) & splitB
					le->setCaret = min
					le->m_selection = -1
					le->countTextRows()
				end if
			end if
		case fb.SC_BACKSPACE
			if len( le->text ) then
				if le->m_selection = -1 then
					splitA = Mid( le->text, 1, le->caret-1 )
					splitB = Mid( le->text, le->caret+1 )
					le->setText = splitA & splitB
					if len( splitA & splitB ) = 0 then
						le->m_selection = -1
					end if
					le->moveCaretLeft()
				else
					min = iif( le->caret < le->m_selection, le->caret, le->m_selection )
					max = iif( le->caret > le->m_selection, le->caret, le->m_selection )
					splitA = Mid( le->text, 1, min-1 )
					splitB = Mid( le->text, max )
					le->setText = splitA & splitB
					le->setCaret = min
					le->m_selection = -1
				end if
			end if
			le->countTextRows()
		case fb.SC_DELETE
			if len( le->text ) then
				if le->m_selection = -1 then
					splitA = Mid( le->text, 1, le->caret )
					splitB = Mid( le->text, le->caret+1 )
					le->setText = splitA & splitB
					if len( splitA & splitB ) = 0 then
						le->m_selection = -1
					end if
				else
					min = iif( le->caret < le->m_selection, le->caret, le->m_selection )
					max = iif( le->caret > le->m_selection, le->caret, le->m_selection )
					splitA = Mid( le->text, 1, min-1 )
					splitB = Mid( le->text, max )
					le->setText = splitA & splitB
					le->setCaret = min
					le->m_selection = -1
				end if
			end if
			le->countTextRows()
		case fb.SC_LEFT
			if len( le->text ) then
				if multikey( fb.SC_LSHIFT ) Or multikey( fb.SC_RSHIFT ) then
					if ( le->m_selection = -1 ) then le->m_selection = le->m_caret
				else
					le->m_selection = -1
				end if
				le->moveCaretLeft()
			end if
		case fb.SC_RIGHT
			if len( le->text ) then
				if multikey( fb.SC_LSHIFT ) Or multikey( fb.SC_RSHIFT ) then
					if ( le->m_selection = -1 ) then le->m_selection = le->m_caret
				else
					le->m_selection = -1
				end if
				le->moveCaretRight()
			end if
		case fb.SC_HOME
			le->m_textStartAt = 1
			le->setCaret = 0
		case fb.SC_END
			max = len( le->text ) - le->columns
			If max < 1 Then
				le->m_textStartAt = 1
				le->setCaret = len( le->text )
			Else
				le->m_textStartAt = max
				le->setCaret = le->columns
			End If
		end select
	end select
	end if

	return 0
	
ENDSLOT

Sub LineEdit.moveCaretLeft()
	m_caret -= 1
	if multiline=0 then
		''ConsolePrint( "moveCaretRight():no multiline" )
		if m_caret < m_textStartAt then
			m_textStartAt -= 1
			if m_textStartAt < 1 then m_textStartAt = 1
		end if
	else
		''ConsolePrint( "moveCaretRight():multiline" )
		/'if m_caret < m_strRows( m_caretRow ).start then
			m_caretRow -= 1
			if m_caretRow < 0 then m_caretRow = 0
		end if'/
		/'with m_strRows( m_caretRow )
			if .start > m_caret then
				moveCaretUp()
			end if
		end with'/
	end if
	if m_caret < 0 then m_caret = 0
End Sub

Sub LineEdit.moveCaretRight()
	var textLen = len( m_text )
	m_caret += 1
	if multiline = 0 then
		''ConsolePrint( "moveCaretRight():no multiline" )
		if m_caret >= (m_textStartAt+columns)-1 then
			m_textStartAt += 1
			if m_textStartAt >= (textLen-columns) then m_textStartAt = (textLen-columns)+1
		end if
	else
		''ConsolePrint( "moveCaretRight():multiline" )
		''with m_strRows( m_caretRow )
		''end with
		
	end if
	if m_caret > len( m_text ) then
		m_caret = len( m_text )
	end if
End sub

sub LineEdit.countTextRows()
	m_rowCount = 0
	if (multiLine = 0) then
		m_rowCount = 1
		m_strRows(0).text = m_text
		m_strRows(0).start = 1
		m_strRows(0).length = len( m_text )
		return
	end if
	dim as integer lp = 1
	dim as integer textlen = len( m_text )
	dim as integer lastToken = 0
	dim as integer startAt = m_textStartAt-1
	ConsolePrint( "Finding lines, starting at " & m_textStartAt )
	for c as integer = m_textStartAt-1 to (m_textStartAt-1)+(rows*columns)
		if c >= textlen-1 then
			exit for
		end if
		select case m_text[c]
			case 32
				ConsolePrint( "Word [" & lastToken & ", " & c+1 & "]=" & mid( m_text, iif( lastToken < 1, 1, lastToken ), c-lastToken+1 ) )
				if c-startAt >= columns-1 then
					m_rowCount += 1
					with m_strRows( m_rowCount-1 )
						.start = startAt+1
						.length = (lastToken-startAt)
						.text = mid( m_text, .start, .length )
					end with
					ConsolePrint( " ^ Forces newline (" & m_rowCount & ")" )
					startAt=lastToken
				end if
				lastToken = c + 1
			case 13
				m_rowCount += 1
				with m_strRows( m_rowCount-1 )
					.start = startAt+1
					.length = (c-startAt)
					.text = mid( m_text, .start, .length )
				end with
				startAt = c
				lastToken = c + 1
		end select
	next c
	if startAt < textlen then
		m_rowCount += 1
		with m_strRows( m_rowCount-1 )
			.start = startAt+1
			.length = (textLen - .start)+1
			.text = mid( m_text, .start, .length )
		end with
	end if
end sub

sub LineEdit.moveCaretUp()
	if m_caretRow > 0 then
		var position = m_caret - m_strRows( m_caretRow ).start
		with m_strRows( m_caretRow-1 )
			var prevRow = .start
			m_caret = iif( position < .length, prevRow + position, prevRow+.length )
		end with
		m_caretRow -= 1
		if m_caretRow < m_rowStartAt then m_rowStartAt = m_caretRow
	end if
end sub

sub LineEdit.moveCaretDown()
	if m_caretRow < m_rowCount then
		var position = m_caret - m_strRows( m_caretRow ).start
		with m_strRows( m_caretRow+1 )
			var nextRow = .start
			m_caret = iif( position < .length, nextRow + position, nextRow+.length )
		end with
		m_caretRow += 1
		if m_caretRow > (m_rowStartAt+rows) then m_rowStartAt += 1
	end if
end sub

end namespace
